package handler

import (
	"encoding/json"
	//"fmt"

	"fmt"
	"iot-base/common/cache"
	calarm "iot-base/common/cache/alarm"
	cpt "iot-base/common/cache/picktemplate"
	"iot-base/common/config"
	"iot-base/common/dbconn"
	"iot-base/common/eventtype"
	"iot-base/common/logger"
	"iot-base/common/notify"
	"time"

	"gorm.io/gorm"

	"strings"
)

var alarmObserved eventtype.AlarmKey

func SetAlarmObserved(tid, alarmid string, index int64) {
	alarmObserved.TID = tid
	alarmObserved.AlarmID = alarmid
	alarmObserved.Mindex = index
}

//const const_alarms = {"system","offline",}
func checkAlarm(curAlarm *eventtype.Alarm) (bool, *cache.ThingInfo, *cache.EventInfo) {
	if curAlarm == nil {
		return false, nil, nil
	}
	if curAlarm.TID == "" || curAlarm.AlarmID == "" {
		logger.SLog.Error("-------checkalarm return false- id is empty-------", *curAlarm)
		return false, nil, nil
	}
	pThing, _ := cache.GetThing(curAlarm.TID)
	if pThing == nil {
		logger.SLog.Error("-------checkalarm return false-- thing not found-----", *curAlarm)
		return false, pThing, nil
	}
	curAlarm.ThingName = pThing.Thing.SName
	now := time.Now()
	if curAlarm.ClearTime == 0 && curAlarm.Flag == "END" {
		curAlarm.ClearTime = now.Unix()
	}
	if curAlarm.Flag == "BEGIN" && curAlarm.Time == 0 {
		curAlarm.Time = now.Unix()
	}
	if curAlarm.AlarmID == "offline" && curAlarm.AlarmName == "" {
		curAlarm.AlarmName = "设备断网"

	} else if strings.HasPrefix(curAlarm.AlarmID, "system/") {
		parts := strings.Split(curAlarm.AlarmID, "/")

		if len(parts) == 2 {
			curAlarm.AlarmName = parts[1] + " 服务启动失败告警"
			return true, pThing, nil
		}
	}

	model, _ := cache.GetModelOption(pThing.Thing.ModelID)

	if model != nil {
		curAlarm.CatalogID = model.CatalogID
	}

	_, pInfo := cache.GetEventInfo(pThing.Thing.ModelID, curAlarm.AlarmID)
	if pInfo != nil {
		if curAlarm.AlarmName == "" {
			curAlarm.AlarmName = pInfo.Name
		}
		if curAlarm.Level == 0 {
			//var delay int64 = 0
			if pInfo == nil {
				curAlarm.Level = pThing.Thing.Level
				//pAlarmDB.Delay = 0
				//curAlarm.Delay = 0
			} else {
				curAlarm.Level = pInfo.Level
				//curAlarm.Delay = pInfo.Delay
				//pAlarmDB.Delay = pInfo.Delay
			}

		}

	} else {
		logger.SLog.Error("pinfo is nil", pThing.Thing.ModelID, curAlarm.AlarmID)
	}

	return true, pThing, pInfo

	//获取告警信息
}

//HandleAlarm 处理告警
func HandleAlarm(curAlarm *eventtype.Alarm) {

	enable := cpt.IsAlarmEnable(curAlarm.TID, curAlarm.AlarmID)
	if !enable {
		return
	}
	ok, pThing, pInfo := checkAlarm(curAlarm)
	if !ok {
		return
	}
	isShield := calarm.IsShield(curAlarm.TID, curAlarm.AlarmID, curAlarm.Time)
	if isShield { //被过滤的告警一律不放行
		return
	}
	_, alarmTime := calarm.GetAlarmRefreshedTime(curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex)

	if alarmTime >= curAlarm.Time {
		return
	}
	if alarmTime > (time.Now().Unix() - 10*60) { // 不算旧，则看isIncache
		isInCache := calarm.IsAlarmState(curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex, nil)
		if isInCache {
			calarm.SaveAlarmTime(curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex, curAlarm.Time)
			return
		}
	} else {
		alarmTime = 0
	}

	var err error
	var pAlarmDB *eventtype.AlarmState
	dbErr := dbconn.DBConnection.Transaction(func(tx *gorm.DB) error {
		pAlarmDB, err = calarm.GetAlarmFromDB(curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex, tx)
		if pAlarmDB != nil {
			defer calarm.FreeAlarm(pAlarmDB)
			pAlarmDB.IsHide = curAlarm.IsHide
		}
		if err == nil {
			if pAlarmDB.RefreshedTime > curAlarm.Time {
				calarm.SaveAlarm(pAlarmDB)
				return nil
			}
			if pAlarmDB.IsAlarm {
				pAlarmDB.RefreshedTime = curAlarm.Time
				calarm.SaveAlarm(pAlarmDB)

				return nil
			}

		} else {
			pAlarmDB.TID = curAlarm.TID
			pAlarmDB.AlarmID = curAlarm.AlarmID
			pAlarmDB.Mindex = curAlarm.Mindex
		}
		pAlarmDB.TimeChanged = eventtype.Datetime(time.Unix(curAlarm.Time, 0))
		pAlarmDB.RefreshedTime = curAlarm.Time

		curAlarm.StationID = pThing.StationID
		curAlarm.StationName = pThing.GetStationName()
		//curAlarm.CatalogID = model.CatalogID
		if curAlarm.Level == 0 {
			curAlarm.Level = 2
		}
		//var delay int64 = 0
		if pInfo == nil {
			pAlarmDB.Delay = 0
			//curAlarm.Delay = 0
		} else {
			//curAlarm.Delay = pInfo.Delay
			pAlarmDB.Delay = pInfo.Delay
		}

		if curAlarm.Info.Params == nil {
			curAlarm.Info.Params = map[string]interface{}{"reason": curAlarm.AlarmName}
		} else {
			r, ok := curAlarm.Info.Params["reason"]
			isEmpty := false
			isEmpty = !ok
			isEmpty = isEmpty || r == nil
			if !isEmpty {
				text, ok := r.(string)
				if !ok || len(text) == 0 {
					isEmpty = true
				}
			}
			if isEmpty {
				curAlarm.Info.Params["reason"] = curAlarm.AlarmName
			}
		}

		jobj, _ := json.Marshal(curAlarm)
		pAlarmDB.AlarmObj = jobj
		pAlarmDB.Level = curAlarm.Level

		//pAlarmDB.TimeChanged = eventtype.DateTime(curAlarm.curAlarm.Time)
		pAlarmDB.ThingName = curAlarm.ThingName
		pAlarmDB.StationID = curAlarm.StationID
		pAlarmDB.StationName = curAlarm.StationName
		pAlarmDB.AlarmName = curAlarm.AlarmName
		pAlarmDB.IsAlarm = true
		err = tx.Save(pAlarmDB).Error
		if err == nil {
			calarm.SaveAlarm(pAlarmDB)
		}

		return err

	})
	if dbErr != nil {
		logger.SLog.Error("save db error ----------", dbErr)
	} else {
		notify.PubMsg(false, "alarm", curAlarm)
		if curAlarm.AlarmID == "offline" {
			if config.PType == "cloud" {
				if pThing.Thing.ProxyThingID == "" {
					cache.SetThingState(curAlarm.TID,
						"offline", time.Time(pAlarmDB.TimeChanged))
				}
			}
		}
	}

}

func handleClearDelay(curAlarm *eventtype.Alarm, delay int64) {

	ok, pThing, pInfo := checkAlarm(curAlarm)
	if !ok {
		logger.SLog.Warn("alarm check error", curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex)
		return
	}
	/*isShield := calarm.IsShield(curAlarm.TID, curAlarm.AlarmID, curAlarm.ClearTime)
	if isShield { //被过滤的告警一律不放行
		return
	}*/

	_, alarmTime := calarm.GetAlarmRefreshedTime(curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex)
	if alarmTime >= curAlarm.ClearTime {
		//calarm.FreeAlarm()
		logger.SLog.Warn("found alarm time wrong-------cleartime is older then redis time", curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex)
		return
	}

	isAlarm := calarm.IsAlarmState(curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex, nil)
	if !isAlarm { //redis状态是消除，更新时间
		calarm.SaveAlarmTime(curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex, curAlarm.ClearTime)
		if alarmTime > (time.Now().Unix() - 5*60) { //redis时间比较新
			logger.SLog.Info("redis时间比较新 放弃本告警", alarmTime,
				curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex)
			return
		}
		logger.SLog.Warn("redis 时间比较旧，检查告警继续", alarmTime,
			curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex)
		//return 进一步看一下数据库是否需要同步
	}

	if delay > 0 {
		handleDelay(curAlarm, delay)
		logger.SLog.Warn("alarm delay-------", alarmTime,
			curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex)
		return
	}
	if pInfo != nil && delay != 0 {
		if pInfo.Delay > 0 {
			logger.SLog.Warn("alarm delay-------", alarmTime,
				curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex)
			handleDelay(curAlarm, pInfo.Delay)
			return
		}
	}

	var err error
	var pAlarmDB *eventtype.AlarmState
	var oldAlarm []byte
	var alarmOldTime eventtype.Datetime
	isHasHistroy := true
	dbErr := dbconn.DBConnection.Transaction(func(tx *gorm.DB) error {
		pAlarmDB, err = calarm.GetAlarmFromDB(curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex, tx)

		if err != nil { //不存在，此时无法记录历史告警
			calarm.SaveAlarmTime(curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex, curAlarm.ClearTime)
			calarm.ClearAlarmCache(curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex)
			fmt.Println("finished---------clear---------", curAlarm.TID, curAlarm.AlarmID, curAlarm.Mindex, err)
			isHasHistroy = false
			//return nil
		} else if pAlarmDB.RefreshedTime > curAlarm.ClearTime {
			calarm.SaveAlarm(pAlarmDB) //告警太旧无意义
			isHasHistroy = false

		}

		if !pAlarmDB.IsAlarm { //数据库为消除
			calarm.SaveAlarmTime(curAlarm.TID, curAlarm.AlarmID,
				curAlarm.Mindex, curAlarm.ClearTime)
			isHasHistroy = false
			return nil
		}
		alarmOldTime = pAlarmDB.TimeChanged
		pAlarmDB.TimeChanged = eventtype.Datetime(time.Unix(curAlarm.ClearTime, 0))
		pAlarmDB.RefreshedTime = curAlarm.ClearTime
		curAlarm.ThingName = pThing.Thing.SName
		curAlarm.StationID = pThing.StationID
		curAlarm.StationName = pThing.GetStationName()
		pAlarmDB.IsAlarm = false
		//pAlarmDB.AlarmName = curAlarm.AlarmName
		jobj, _ := json.Marshal(curAlarm)
		oldAlarm = pAlarmDB.AlarmObj
		pAlarmDB.AlarmObj = jobj
		isHasHistroy = true
		err = tx.Save(pAlarmDB).Error
		if err == nil {
			notify.PubMsg(false, "alarm", curAlarm)
			calarm.SaveAlarm(pAlarmDB)
		}
		return err

	})
	if pAlarmDB != nil {
		defer calarm.FreeAlarm(pAlarmDB)
	}
	if curAlarm.AlarmID == "offline" {
		if config.PType == "cloud" {
			if pThing.Thing.ProxyThingID == "" {
				cache.SetThingState(curAlarm.TID,
					"online", time.Now())
			}
		}
	}
	if dbErr != nil {
		logger.SLog.Error("save db error ----------", dbErr)
	} else {

		if isHasHistroy {
			var alarmHistroy eventtype.AlarmHistroy
			alarmHistroy.Seq = pAlarmDB.UID
			alarmHistroy.AlarmKey = pAlarmDB.AlarmKey
			alarmHistroy.AlarmTime = alarmOldTime
			alarmHistroy.ClearTime = pAlarmDB.TimeChanged
			alarmHistroy.AlarmName = pAlarmDB.AlarmName
			alarmHistroy.AlarmObj = oldAlarm
			alarmHistroy.ClearObj = pAlarmDB.AlarmObj
			alarmHistroy.AlarmDisp = pAlarmDB.AlarmDisp
			alarmHistroy.StationName = curAlarm.StationName
			alarmHistroy.StationID = curAlarm.StationID
			alarmHistroy.CatalogID = curAlarm.CatalogID
			alarmHistroy.IsHide = curAlarm.IsHide
			errdb := dbconn.DBConnection.Create(&alarmHistroy).Error
			if errdb != nil {
				logger.SLog.Error("alarmhistroy save error", errdb)
			}
		}

	}
}

//HandleClear 处理告警消除
func HandleClear(curAlarm *eventtype.Alarm) {
	handleClearDelay(curAlarm, 0)
}
